package com.lagou.java;

import com.alibaba.fastjson.JSON;
import lombok.Cleanup;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.RetryNTimes;
import org.apache.zookeeper.ClientCnxn;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Classname ZkBoostrap
 * @Description TODO
 * @Date 2020/8/20 12:59
 * @Created by kaifa
 */
@Slf4j
public class ZkBoostrap<T> {

    public static final String SERVET_PREFIX = "/NETTY-SERVER-";

    private static volatile   CuratorFramework client;

    private static volatile Lock lock = new ReentrantLock();

    public ZkBoostrap() {
        lock.lock();
        try {
            if (this.client == null) {
                this.client = CuratorFrameworkFactory.newClient(ZkPathEnum.SERVER_ADDR, new RetryNTimes(3, 1000));
                this.client.start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 创建节点
     * @param serverInfo
     */
    public String createNode(ServerInfo serverInfo) {
        String node = null;
        try {
            node = ZkPathEnum.BASE_SERVER_PATH + SERVET_PREFIX + serverInfo.getAddress();
            create(node, JSON.toJSON(serverInfo));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return node;
    }

    public String create(String node, Object data) {
        try {
            Stat stat = client.checkExists().forPath(node);
            if (stat != null) {
                delete(node);
            }
            client.create()
                    .creatingParentsIfNeeded()
                    .withMode(CreateMode.EPHEMERAL)
                    .forPath(node, data.toString().getBytes());
            log.info("===>>> 服务端添加新的服务端点：{}，节点信信息详情：{}", node, data);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return node;
    }

    // 修改服务
    public String setNode(String node, String address, int timeout) throws Exception {
        String[] split = address.split(":");
        ServerInfo serverInfo = new ServerInfo();
        serverInfo.setHost(split[0]);
        serverInfo.setPort(Integer.parseInt(split[1]));
        serverInfo.setExpiredTime(timeout);
        return set(node, serverInfo);
    }

    public String set(String node, Object data) {
        try {
            Stat stat = client.checkExists().forPath(node);
            if (stat != null) {
                byte[] bytes = client.getData().forPath(node);
                client.setData()
                        .forPath(node, JSON.toJSONString(data).getBytes());
                log.info("===>>> 服务端节点信息修改, 端点：{}，节点信信息详情：{}, 修改前信息为： {}", node, data, new String(bytes) );
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return node;
    }




    // 添加服务
    public String addNode(String address) throws Exception {
        String[] split = address.split(":");
        ServerInfo serverInfo = new ServerInfo();
        serverInfo.setHost(split[0]);
        serverInfo.setPort(Integer.parseInt(split[1]));
        return createNode(serverInfo);
    }

    public Map<String, ServerInfo> getAllServerNodes()  throws Exception{
        Map<String, ServerInfo> serverInfoMap = new HashMap<>();
        List<String> nodeList = getClient().getChildren().forPath(ZkPathEnum.BASE_SERVER_PATH);
        for (String node : nodeList) {
            byte[] bytes = getClient().getData().forPath(ZkPathEnum.BASE_SERVER_PATH);
            ServerInfo serverInfo = JSON.parseObject(bytes, ServerInfo.class);
            serverInfoMap.put(node, serverInfo);
        }

        return serverInfoMap;
    }

    // 关闭服务
    public void deleteNode (String address) throws Exception{
        Map<String, ServerInfo> allServerNodes = getAllServerNodes();
        boolean deleted = false;
        for (Map.Entry<String, ServerInfo> entry : allServerNodes.entrySet()) {
            String node = "/" + entry.getKey();

            Stat stat = client.checkExists().forPath(node);
            if (entry.getValue() != null &&
                    entry.getValue().getAddress() != null &&
                    entry.getValue().getAddress().equals(address)
                    && stat != null) {
                delete(node);
                deleted = true;
                log.info("服务节点：{}被删除， 详细信息为：{}", entry.getKey(), entry.getValue());
            }
        }
        if (!deleted) {
            log.warn("地址为 {} 的服务节点不存在， 无法删除",  address);
        }

    }

    // 关闭服务
    public void delete (String node) throws Exception{
        Stat stat = client.checkExists().forPath(node);
        if (stat != null) {
            byte[] bytes = client.getData().forPath(node);
            getClient().delete().deletingChildrenIfNeeded().forPath(node);
            log.info("服务节点：{}被删除， 详细信息为：{}", node, new String(bytes));
        } else {
            log.warn("服务节点：{} 不存在", node);
        }
    }




    /**
     * 保存配置信息
     */
    public void updateNodeData() {
//        Object properties = AppContextUtil.getObject("properties");
//        Properties myProperties = (Properties)properties;
//        String configFullName = Constants.NETTY_ROOT_PATH + Constants.NETTY_SERVER_NODE + "_"
//                + myProperties.getUrl() + "_"
//                + myProperties.getPort();
//        try {
//            byte[] bytes = client.getData().forPath(configFullName);
//            ServerClientVo serverClientVo = JSON.parseObject(new String(bytes), ServerClientVo.class);
//            serverClientVo.setExpireTime(System.currentTimeMillis());
//            client.setData().forPath(configFullName,JSON.toJSONBytes(serverClientVo));
//        } catch (Exception e) {
//            e.printStackTrace();
//        }

    }

    public CuratorFramework getClient() {
        return client;
    }




}
